#!/usr/bin/python
# -*- coding: utf-8 -*-

# @File        :   get_weather.py
# @Version     :   1.0
# @Author      :   zhfnss
# @Email       :   mklv2005@163.com
# @Time        :   2021/03/30 11:10:10
# Description  :

import json
import multiprocessing
import random
import time
from concurrent.futures import ProcessPoolExecutor, as_completed
from datetime import datetime, timedelta

from bs4 import BeautifulSoup
from sqlalchemy import and_

from model.weather_city import WeatherCity
from model.weather_info import WeatherInfo
from model.weather_flag import WeatherFlag
from task_init import get_city_task_list, get_his_task_list
from utils.db_util import db
from utils.html_util import html_util

# 中文的星期
week_cn = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]

week_dict = {
    "周一": 0, "周二": 1, "周三": 2, "周四": 3, "周五": 4, "周六": 5, "周日": 6
}


def aqi_level(aqi):
    """
    空气质量指数级别
    参考https://baike.baidu.com/item/AQI/463245?fr=aladdin
    """
    d = {
        '优': 50,
        '良': 100,
        '轻度': 150,
        '中度': 200,
        '重度': 300,
        '严重': 350
    }
    return d[aqi]


def get_seven_day_weather_url(city_id):
    """最近7天天气url"""
    return 'http://tianqi.2345.com/today-%s.htm' % city_id


# 模拟浏览器访问
header = {
    'Accept': 'application/json, text/javascript, */*; q=0.01',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Connection': 'keep-alive',
    'Host': 'tianqi.2345.com',
    'Referer': 'http://tianqi.2345.com/',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36',
    'X-Requested-With': 'XMLHttpRequest'
}


def update_last_synch_date(city_id, last_synch_date):
    """更新最近同步日期"""
    rs = db.session.query(WeatherCity).filter(
        and_(
            WeatherCity.city_id == city_id,
            # 增加条件，用于多线程，防止重复抓取历史月份的数据
            WeatherCity.last_synch_date < datetime.strptime(last_synch_date, '%Y-%m-%d')
        )
    ).update(
        {
            WeatherCity.last_synch_date: datetime.strptime(last_synch_date, '%Y-%m-%d')
        }
    )
    print('update_last_synch_date', '---------------->', rs)
    db.session.commit()


def insert_weather_flag(weather_flag):
    """如果不存在天气名称就保存，并返回id"""
    flag_id = db.weather_flag_exists(weather_flag)
    if not flag_id:
        wf = WeatherFlag(flag_name=weather_flag)
        db.session.add(wf)
        db.session.commit()
        flag_id = db.weather_flag_exists(weather_flag)
    return flag_id


def insert_weather(weather_info, is_his=False):
    """
    将天气保存到db，存在则更新否则新增
    is_his=False，插入的是否是历史天气信息，如果是的则更新weather_city表中的最近同步日期，否则不更新
    """
    if db.weather_exists(weather_info.city_id, weather_info.weather_date):
        db.session.query(WeatherInfo).filter(
            and_(
                WeatherInfo.city_id == weather_info.city_id,
                WeatherInfo.weather_date == weather_info.weather_date
            )
        ).update(
            {
                WeatherInfo.city_id: weather_info.city_id,
                WeatherInfo.weather_date: weather_info.weather_date,
                WeatherInfo.weather_week: weather_info.weather_week,
                WeatherInfo.high_temp: weather_info.high_temp,
                WeatherInfo.low_temp: weather_info.low_temp,
                WeatherInfo.weather: weather_info.weather,
                WeatherInfo.wind_dir: weather_info.wind_dir,
                WeatherInfo.pm25: weather_info.pm25,
                WeatherInfo.aqi: weather_info.aqi
            }
        )
    else:
        db.session.add(weather_info)

    # 插入的是否是历史天气信息，如果是的则更新weather_city表中的最近同步日期，否则不更新
    if is_his:
        update_last_synch_date(weather_info.city_id, weather_info.weather_date)

    db.session.commit()


def save_his_weather(city_id, year, month, batch=False):
    """爬取并保存历史天气信息，按年月"""
    url = 'http://tianqi.2345.com/Pc/GetHistory'
    data = {
        'areaInfo[areaId]': city_id,
        'areaInfo[areaType]': 2,
        'date[year]': year,
        'date[month]': month
    }
    header['Referer'] = 'http://tianqi.2345.com/wea_history/{}.htm'.format(city_id)
    res = html_util.get(url, data=data, header=header)
    js = json.loads(res)
    soup = BeautifulSoup(js['data'], "html.parser")
    # 天气信息列数，用来判断换行
    col_num = len(soup.select('th'))

    td_list = soup.select('td')

    l_w = []
    i = 0
    for td in td_list:
        if (i % col_num) == 0:
            wi = WeatherInfo(city_id=city_id)
            wi.weather_date = td.string.split(' ')[0]
            wi.weather_week = week_dict.get(td.string.split(' ')[1], -1)
        elif (i % col_num) == 1:
            wi.high_temp = None if td.string.replace('°', '') == '' else td.string.replace('°', '')
        elif (i % col_num) == 2:
            wi.low_temp = None if td.string.replace('°', '') == '' else td.string.replace('°', '')
        elif (i % col_num) == 3:
            wi.weather = insert_weather_flag(td.string)
        elif (i % col_num) == 4:
            # 处理以前没有aqi的情况
            wi.wind_dir = td.string
            if (i % col_num) == (col_num - 1):
                l_w.append(wi)
        elif (i % col_num) == (col_num - 1):
            # 处理有aqi的情况
            if td.string != '-':
                wi.pm25 = td.string.split(' ')[0]
                wi.aqi = td.string.split(' ')[1]
            l_w.append(wi)
        i += 1

    for wi in l_w:
        insert_weather(wi, is_his=True)

    if batch:
        time.sleep(random.uniform(0, 1))


def save_seven_day_weather(city_id):
    """保存最近七天天气信息"""
    url = get_seven_day_weather_url(city_id)
    res = html_util.get(url, header=header)
    soup = BeautifulSoup(res, "html.parser")
    seven_day_weather = soup.find('div', class_='seven-day')
    now = datetime.now()
    l = []
    # 0下标数据为昨天天气不再处理，所以用range(1, 8)来遍历
    for i in range(1, 8):
        wi = WeatherInfo(city_id=city_id)
        day_weather = seven_day_weather.select('li')[i]
        _now = now + timedelta(days=(i - 1))
        wi.weather_date = _now.strftime('%Y-%m-%d')
        # wi.weather_week = week_cn[_now.weekday()]
        wi.weather_week = _now.weekday()
        wi.low_temp = day_weather.select('.tem-show')[0].string.split('~')[0]
        wi.high_temp = day_weather.select('.tem-show')[0].string.split('~')[1].replace('°', '')
        wi.weather = insert_weather_flag(day_weather.select('i')[0].string.replace('转', '~'))
        # pm25大概值，在历史天气数据中进行准确数据更新
        wi.pm25 = aqi_level(day_weather.select('.wea-qulity')[0].string)
        wi.aqi = day_weather.select('.wea-qulity')[0].string
        wi.wind_dir = day_weather.select('.wind-name')[0].string
        l.append(wi)

    for wi in l:
        insert_weather(wi, is_his=False)


if __name__ == "__main__":
    # 最近7天天气
    city_list = get_city_task_list()
    with ProcessPoolExecutor(max_workers=multiprocessing.cpu_count()) as executor:
        futures = {
            executor.submit(save_seven_day_weather, city['city_id']): city for city in city_list}
        for f in as_completed(futures):
            try:
                print(futures[f])
            except Exception as e:
                print(e)

    # 刷新最近一个月的历史天气数据
    his_task_list = get_his_task_list()
    with ProcessPoolExecutor(max_workers=multiprocessing.cpu_count()) as executor:
        futures = {
            executor.submit(save_his_weather, his_task['city_id'], his_task['year'], his_task['month'], True): his_task
            for his_task in his_task_list}
        for f in as_completed(futures):
            try:
                print(futures[f])
            except Exception as e:
                print(e)
